#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import json
import socket

from common.cleaner import clear
from common.file_common import log
from hcs.gaussdb.common.gaussdb_common import get_std_in_variable, get_input_dict_in_variable
from hcs.gaussdb.common.const import EnvName, JobInfo, RoachConstant, BACKUP_RATE_LIMIT_MIN, BACKUP_RATE_LIMIT_MAX, \
    BACKUP_RATE_LIMIT_NORMAL
from hcs.gaussdb.common.gaussdb_common import get_std_in_variable
from hcs.gaussdb.common.gaussdb_restful import request_post, request_get, request_put


class ResourceInfo:

    def __init__(self, pid, data, addr, project_name, project_domain, business_addr, project_id):
        self.pid = pid
        self._data = data
        self._addr = addr
        self._project_name = project_name
        self._project_id = project_id
        self._project_domain = project_domain
        self._business_addr = business_addr
        input_dict = get_input_dict_in_variable()
        self.user_name = input_dict.get(f"{EnvName.IAM_USERNAME}_{pid}")
        if input_dict.get(f"job_protectEnv_extendInfo_ca_cert_pem_{pid}"):
            self.ca_cert_pem = input_dict.get(f"job_protectEnv_extendInfo_caCertPem_{pid}")
            self.client_key = input_dict.get(f"job_protectEnv_extendInfo_clientKey_{pid}")
            self.rand_pass = input_dict.get(f"job_protectEnv_auth_extendInfo_randPass_{pid}")
        elif input_dict.get(f"job_targetObject_extendInfo_caCertPem_{pid}"):
            self.ca_cert_pem = input_dict.get(f"job_targetObject_extendInfo_caCertPem_{pid}")
            self.client_key = input_dict.get(f"job_targetObject_extendInfo_clientKey_{pid}")
            self.rand_pass = input_dict.get(f"job_targetObject_auth_extendInfo_randPass_{pid}")
        else:
            self.ca_cert_pem = ""
            self.client_key = ""
            self.rand_pass = ""
        self.restore_to_new_password = input_dict.get(f"job_extendInfo_password_{pid}")

    @staticmethod
    def get_hostname():
        return socket.gethostname()

    @staticmethod
    def get_tokens_json(job_info: JobInfo):
        token_body = {
            "auth": {
                "identity": {
                    "methods": ["password"],
                    "password": {"user": {"name": "", "password": "", "domain": {"name": ""}}}
                },
                "scope": {"project": {"name": "", "domain": {"name": ""}}}
            }
        }
        token_header = {'Content-Type': 'application/json'}
        # 待确定管控面端口是否固定
        url = f"{job_info.addr}/v3/auth/tokens"
        user_password = get_std_in_variable(f"{EnvName.IAM_PASSWORD}_{job_info.pid}")
        try:
            token_body["auth"]["identity"]["password"]["user"]["name"] = job_info.user_name
            token_body["auth"]["identity"]["password"]["user"]["password"] = user_password
            token_body["auth"]["identity"]["password"]["user"]["domain"]["name"] = job_info.project_domain
            token_body["auth"]["scope"]["project"]["name"] = job_info.project_name
            token_body["auth"]["scope"]["project"]["domain"]["name"] = job_info.project_domain
        except KeyError as err:
            raise Exception(f"Failed to get correct key from the dict") from err
        ret, body, header = request_post(url, token_body, token_header)
        clear(user_password)
        return ret, body, header

    @staticmethod
    def config_extend_info(host, port, xbsa_file_path):
        # 兼容老版本，当xbsa_file_path字段没传时，取默认值
        if xbsa_file_path == '':
            log.info("Roach xbsa conf path is none")
            xbsa_file_path = RoachConstant.XBSA_FILE_DEFAULT_PATH
        token_body_backupset = {"key": "BackupSet", "value": xbsa_file_path}
        token_body_route_value = {"ip": host, "port": port}
        token_body_route = {"key": "XbsaRoute", "value": json.dumps(token_body_route_value)}
        return token_body_backupset, token_body_route

    def get_job_info(self):
        job_info = JobInfo(copy_id="",
                           job_id="",
                           pid=self.pid,
                           user_name=self.user_name,
                           project_domain=self._project_domain,
                           project_name=self._project_name,
                           addr=self._addr)
        return job_info

    def get_token_info(self):
        ret, body, header = ResourceInfo.get_tokens_json(self.get_job_info())
        return ret, body, header

    def get_instance_json(self):
        token = self.get_token()
        header = {'X-Auth-Token': token, 'X-Language': 'en-us'}
        url = f'{self._business_addr}/gaussdb/v3/{self._project_id}/instances'
        ret, ret_body, ret_header = request_get(url, {}, header)
        clear(token)
        if not ret:
            return {}
        return ret_body

    def set_instance_extend_info(self, instance_id, token_body):
        token = self.get_token()
        header = {'X-Auth-Token': token, 'X-Language': 'en-us'}
        url = f'{self._business_addr}/gaussdb/v3/{self._project_id}/instances/{instance_id}/extend-info'
        log.info(f"set_instance_extend_info url:{url} token_body.")
        ret, ret_body, ret_header = request_put(url, token_body, header)
        clear(token)
        if not ret:
            return {}
        return ret_body

    def get_token(self):
        ret, body, header = ResourceInfo.get_tokens_json(self.get_job_info())
        if not ret:
            log.error("Failed get Token!")
            return {}
        token = header.get("X-Subject-Token", None)
        if not token:
            log.error("Failed get Token!")
            return {}
        return token

    def set_instance_extend_info_twice(self, instance_id, host, port, client_crt, xbsa_file_path):
        token_body_backupset, token_body_route = self.config_extend_info(host, port, xbsa_file_path)
        self.set_instance_extend_info(instance_id, token_body_backupset)
        self.set_instance_extend_info(instance_id, token_body_route)
        log.info(f"finish to set instance extend info. backup set {token_body_backupset},"
                 f"backup route {token_body_route}")
        if not (client_crt and self.client_key and self.rand_pass and self.ca_cert_pem):
            log.info("Client_crt is none, no need set crt")
            return True
        try:
            self.set_ssl_cert_info(instance_id, client_crt)
        except Exception as ex:
            log.error(f"set_ssl_cert_info failed, exception: {ex}")
        return True

    def set_ssl_cert_info(self, instance_id, client_crt):
        ret, body, header = ResourceInfo.get_tokens_json(self.get_job_info())
        if not ret:
            log.error("Failed get Token!")
            return {}
        token = header.get("X-Subject-Token", None)
        if not token:
            log.error("Failed get Token!")
            return {}
        set_ssl_cert_body = {"ca_cert_pem": "", "client_crt": "", "client_key": "", "rand_pass": ""}
        set_ssl_cert_body["ca_cert_pem"] = self.ca_cert_pem
        set_ssl_cert_body["client_crt"] = client_crt
        set_ssl_cert_body["client_key"] = self.client_key
        set_ssl_cert_body["rand_pass"] = self.rand_pass
        set_ssl_cert_header = {'X-Auth-Token': token, 'X-Language': 'en-us'}
        set_ssl_cert_url = f'{self._business_addr}/gaussdb/v3/' \
                           f'{self._project_id}/instances/{instance_id}/backups/ssl-certs'
        ret, ret_body, ret_header = request_post(set_ssl_cert_url, set_ssl_cert_body, set_ssl_cert_header)
        clear(token)
        log.info(f"set_ssl_cert_info url:{set_ssl_cert_url} ret_body")
        if not ret:
            return {}
        return ret_body

    def set_gaussdb_conf(self, instance_id, backup_policy):
        rate_limit = backup_policy.get("ext_parameters").get("rate_limit")
        enable_standby_backup = backup_policy.get("ext_parameters").get("enable_standby_backup", False)
        close_compression = backup_policy.get("ext_parameters").get("close_compression", False)
        if rate_limit is None:
            log.info(f"Rate limit is None, use default value")
            rate_limit = BACKUP_RATE_LIMIT_NORMAL
        if not all([isinstance(rate_limit, int),
                    isinstance(enable_standby_backup, bool),
                    isinstance(close_compression, bool),
                    BACKUP_RATE_LIMIT_MIN <= rate_limit <= BACKUP_RATE_LIMIT_MAX]):
            log.error("Param error.")
            return False
        token = self.get_token()
        url = f"{self._business_addr}/gaussdb/v3/{self._project_id}/instances/{instance_id}/backups/config"
        request_header = {'X-Auth-Token': token, 'X-Language': 'en-us'}
        request_body = {
            "rate_limit": rate_limit,
            "enable_standby_backup": enable_standby_backup,
            "close_compression": close_compression
        }
        log.info(f"Get request body {request_body}.")
        ret, ret_body, header = request_put(url, request_body, request_header)
        log.info(f"Set gaussdb backup config result: {ret_body}.")
        if not ret or 'DBS.200856' in ret_body:
            # 低数据库引擎版本，不支持设置是否压缩参数
            log.info(f"Failed set gauss db config for instance {instance_id}, start to use lower version!")
            request_body = {
                "rate_limit": rate_limit,
                "enable_standby_backup": enable_standby_backup
            }
            ret, ret_body, header = request_put(url, request_body, request_header)
            log.info(f"Set gaussdb backup config result: {ret_body}.")
            if not ret:
                clear(token)
                raise Exception("Set gauss db config failed.")

        ret, ret_body, header = request_get(url, {}, request_header)
        log.info(f"Get gaussdb backup config info: {ret_body}.")
        clear(token)
        if not ret:
            log.error(f"Failed get gauss db config for instance {instance_id}!")
            raise Exception("Get gauss db config failed.")
        log.info("Set gauss db config successful.")
        return True
